/* 
 * Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *      * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *      * The names of its contributors may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package kameleon.gui.view;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.TitledBorder;

import kameleon.exception.FileReadingException;
import kameleon.gui.exception.InvalidOutputFileException;
import kameleon.gui.exception.UnknownKeyException;
import kameleon.gui.language.SwitchLanguage;
import kameleon.gui.model.FileInfo;
import kameleon.gui.model.Model;
import kameleon.gui.model.Observer;
import kameleon.gui.util.FileConstants;
import kameleon.gui.util.ImageUtility;
import kameleon.gui.util.LanguageConstants;
import kameleon.plugin.PlugInInfo;

/**
 * View showing the available output formats and the generation button.
 * The available formats are displayed using the colored pictures for
 * selected formats and grayed pictures for the others.
 * 
 * @author		Fromentin Xavier, Schnell Michaël
 * @version		1.0
 */
public class GenerationView extends JPanel
implements Observer, FileConstants, LanguageConstants {

	/**
	 * Needed to serialize this class.
	 * 
	 * @see		java.io.Serializable
	 */
	private static final long serialVersionUID = 9147115868827007825L;

	/**
	 * Model of this view.
	 */
	protected Model model ;
	
	/**
	 * View displaying the available formats.
	 */
	protected OutputFormatView formatsView ;
	
	/**
	 * "Generate" button. Launches the generation.
	 */
	protected JButton generate ;
	
	/**
	 * Message displayed when there are no generators installed.
	 */
	protected JLabel emptyMessage ;
	
	/**
	 * Component containing this view.
	 */
	protected Component container ;

	/**
	 * Builds an instance with the given model and parent.
	 * 
	 * @param 	model
	 * 			model for this view
	 * 
	 * @param 	container
	 * 			parent {@code Component} for this view
	 */
	public GenerationView(Model model, Component container) {
		super();
		this.model = model ;
		this.model.addObserver(this) ;
		this.container = container ;
		this.build() ;
	}// GenerationView(Model, Component)

	/**
	 * Builds the content of this view.
	 */
	protected void build() {
		this.setBorder(BorderFactory.createTitledBorder("")) ; //$NON-NLS-1$
		GridBagLayout gridbag = new GridBagLayout() ;
		this.setLayout(gridbag) ;
		GridBagConstraints constraints ;

		// Display the available formats
		constraints = new GridBagConstraints() ;
		constraints.gridwidth = GridBagConstraints.REMAINDER ;
		constraints.fill = GridBagConstraints.HORIZONTAL ;
		this.formatsView = new OutputFormatView(this.model) ;
		this.add(this.formatsView) ;
		gridbag.setConstraints(this.formatsView, constraints) ;

		// Generate button
		constraints = new GridBagConstraints() ;
		constraints.gridwidth = GridBagConstraints.REMAINDER ;
		constraints.anchor = GridBagConstraints.CENTER ;
		this.generate = new JButton() ;
		this.generate.addActionListener(new GenerationListener()) ;
		this.generate.setEnabled(false) ;
		this.add(this.generate) ;
		gridbag.setConstraints(this.generate, constraints) ;
	}// build()

	/**
	 * Updates the displayed output formats and enables/disables the
	 * generate button.
	 * 
	 * @throws 	UnknownKeyException
	 * 			if an error occurred while updating the text of this view
	 */
	@Override
	public void update() throws UnknownKeyException {
		if (this.model.generatorAddedOrRemoved()) {
			this.removeAll() ;
			this.build() ;
			this.reloadLanguage() ;
		} else {
			this.generate.setEnabled(this.model.generationIsPossible());
		}// if
	}// udpate()

	/**
	 * Updates the text to match the currently selected language.
	 * 
	 * @throws 	UnknownKeyException
	 * 			if an error occurred while updating the text of this view
	 */
	@Override
	public void reloadLanguage() throws UnknownKeyException {
		SwitchLanguage sl = SwitchLanguage.getInstance() ;
		TitledBorder border = (TitledBorder) this.getBorder() ;
		border.setTitle(sl.getText(GENERATION_TITLE_BORDER)) ;
		this.generate.setText(sl.getText(GENERATE_BUTTON)) ;
		if (this.emptyMessage != null) {
			this.emptyMessage.setText(sl.getText(NO_GENERATORS)) ;
		}// if
	}// reloadLanguage()

	/**
	 * View displaying the available output formats.
	 * 
	 * <p>Each format is displayed using a coloured/grayed picture based 
	 * on whether the output format is selected or not. Each picture
	 * uses the format name as tooltip.
	 * 
	 * @author	Fromentin Xavier, Schnell Michaël
	 * @version	1.0
	 */
	private class OutputFormatView extends JPanel {

		/**
		 * Needed to serialize this class.
		 * 
		 * @see	java.io.Serializable
		 */
		private static final long serialVersionUID = -640280176195965001L ;
		
		/**
		 * Buttons used to display the available output formats.
		 */
		protected JButton[] formats ;

		/**
		 * Builds an instance with the given model.
		 * 
		 * @param 	model
		 * 			model for this view
		 */
		public OutputFormatView(Model model) {
			super() ;
			this.build(model) ;
		}// OutputFormatView(Model)

		/**
		 * Builds the content of this view.
		 * 
		 * @param 	sourceModel
		 * 			model used to retrieve the available formats
		 */
		protected void build(Model sourceModel) {
			GridBagLayout gridbag = new GridBagLayout() ;
			this.setLayout(gridbag) ;
			GridBagConstraints c ;
			List<PlugInInfo> generators = 
					sourceModel.getKnownGenerators() ;
			
			if (generators.isEmpty()) {
				JLabel info = new JLabel() ;
				try {
					SwitchLanguage sl = SwitchLanguage.getInstance() ;
					info.setText(sl.getText(NO_GENERATORS)) ;
				} catch (UnknownKeyException uke) {
					//TODO Handle exception
					sourceModel.displayDebugInformation(uke) ;
				}// try
				c = new GridBagConstraints() ;
				c.gridwidth = 1 ;
				c.weightx = 1.0 ;
				this.add(info) ;
				gridbag.setConstraints(info, c) ;
				GenerationView.this.emptyMessage = info ;
			} else {
				GenerationView.this.emptyMessage = null ;
				this.formats = new JButton[generators.size()] ;
				int nGenerators = generators.size() ;
				List<String> selectedGenerators = 
						GenerationView.this.model.getSelectedOutputFormats();
				for(int g=0; g < nGenerators; ++g) {
					try {
						PlugInInfo generatorInfo = generators.get(g) ;
						boolean selected = 
								selectedGenerators.contains(generatorInfo.getId());
						String imagePath = String.format(selected ? COLOR_ICON : GRAY_ICON, 
								generatorInfo.getId()) ;
						
						JButton button = new JButton(new ImageIcon(
								ImageUtility.getImageBytes(imagePath))) ;
						button.setContentAreaFilled(false) ;
						button.setBorderPainted(false) ;
						button.setCursor(
								Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)) ;
						button.setToolTipText(generatorInfo.getFormatName());
						button.addActionListener(new FormatSelectionListener(
								g, 
								generatorInfo.getId(),
								button)) ;
						button.setSelected(selected);
						
						c = new GridBagConstraints() ;
						c.gridwidth = 1 ;
						c.weightx = 1.0 ;
						
						this.add(button) ;
						gridbag.setConstraints(button, c) ;
						
						this.formats[g] = button ;
					} catch(FileReadingException fre) {
						//TODO Handle exception
						sourceModel.displayDebugInformation(fre) ;
					}// try
				}// for
			}// if
		}// build(Model)
		
	}// class OutputFormatView

	/**
	 * Listener handling the clicks on the format pictures. Selects
	 * or "de-selects" the output format whose picture was clicked.
	 * 
	 * @author	Fromentin Xavier, Schnell Michaël
	 * @version	1.0
	 */
	private class FormatSelectionListener implements ActionListener {

		/**
		 * Identifier of the handled output format.
		 */
		protected String formatId ;
		
		/**
		 * Button attached to this listener.
		 */
		protected JButton button ;

		/**
		 * Builds an instance with the given values.
		 * 
		 * @param 	iconId
		 * 			identifier of the icon within the containing view
		 * 
		 * @param	formatId
		 * 			identifier of the handled output format
		 * 
		 * @param 	button
		 * 			button attached to this listener
		 */
		public FormatSelectionListener(int iconId, String formatId, 
				JButton button) {
			super() ;
			this.formatId = formatId ;
			this.button = button ;
		}// FormatSelectionListener(int, String, String, JButton)

		/**
		 * Selects or de-selects the handled format.
		 * 
		 * @param	e
		 * 			event which triggered this listener
		 */
		@Override
		public void actionPerformed(ActionEvent e) {
			GenerationView gv = GenerationView.this ;
			try {
				if (this.button.isSelected()) {
					this.button.setIcon(new ImageIcon(ImageUtility.getImageBytes(
							String.format(GRAY_ICON, this.formatId)))) ;
					this.button.setSelected(false) ;
					gv.model.removeSelectedFormat(this.formatId) ;
				} else {
					this.button.setIcon(new ImageIcon(ImageUtility.getImageBytes(
							String.format(COLOR_ICON, this.formatId)))) ;
					this.button.setSelected(true) ;
					gv.model.addSelectedFormat(this.formatId) ;
				}// if
			} catch(FileReadingException fre) {
				//TODO Handle Exception
				gv.model.displayDebugInformation(fre) ;
			}// try
		}// actionPerformed(ActionEvent)

	}// class FormatSelectionListener

	/**
	 * Listener handling the clicks on the generate button. Launches
	 * the "output file selector" window before starting the generation.
	 * 
	 * @author	Fromentin Xavier, Schnell Michaël
	 * @version	1.0
	 */
	private class GenerationListener implements ActionListener {

		/**
		 * File chooser used to determine output folder and the
		 * output file name.
		 */
		protected JFileChooser outputChooser ;

		/**
		 * Sole constructor.
		 */
		public GenerationListener() {
			super() ;
			this.outputChooser = new JFileChooser() ;
		}// GenerationListener()

		/**
		 * Prompt the user to get the output folder and output file
		 * name before launching the generation.
		 * 
		 * @param	e
		 * 			event which triggered the listener
		 */
		@Override
		public void actionPerformed(ActionEvent e) {
			GenerationView gv = GenerationView.this ;
			FileInfo selectedFile = gv.model.getSelectedFileInfo() ;
			
			// Sets the output folder to the folder of the last 
			// generation
			if (selectedFile.getLastGeneration() != null) {
				String outputFile = selectedFile.getTargetPath()[0] ;
				int fileNameStart = 
						outputFile.lastIndexOf(File.separatorChar) ;
				String outputFolder = 
						outputFile.substring(0, fileNameStart) ;
				this.outputChooser.setCurrentDirectory(
						new File(outputFolder)) ;
			}// if
			
			int saveResult = 
					this.outputChooser.showSaveDialog(gv.container) ;
			if (saveResult == JFileChooser.APPROVE_OPTION) {
				try {
					gv.model.setOutputFile(
							this.outputChooser.getSelectedFile()) ;
					gv.model.launchGeneration() ;
				} catch (InvalidOutputFileException iofe) {
					//TODO Handle exception
					gv.model.displayDebugInformation(iofe) ;
				}// try
			}// if
		}// actionPerformed(ActionEvent)
		
	}// class GenerationListener

}// class GenerationView